/// Copyright 2007-8 Faraday Media
/// 
/// Licensed under the Apache License, Version 2.0 (the "License"); 
/// you may not use this file except in compliance with the License. 
/// You may obtain a copy of the License at 
/// 
///   http://www.apache.org/licenses/LICENSE-2.0 
/// 
/// Unless required by applicable law or agreed to in writing, software 
/// distributed under the License is distributed on an "AS IS" BASIS, 
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
/// See the License for the specific language governing permissions and 
/// limitations under the License.
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Reflection;

namespace MetaSharp.XML.Strategies {
  ///<summary>
  /// Strategy for visualising a sequence as a list.
  ///</summary>
  public class SequenceListPropertyStrategy : SequencePropertyStrategy {
    /// <summary>
    /// Checks whether the given strategy applies to a property.
    /// </summary>
    /// <param name="pProp">the property to check</param>
    /// <returns>true - strategies apply</returns>
    public override bool AppliesToProperty(PropertyInfo pProp) {
      return base.AppliesToProperty(pProp) && pProp.PropertyType.IsGenericType && typeof(IList<>).IsAssignableFrom(pProp.PropertyType.GetGenericTypeDefinition());
    }

    #region SequencePropertyStrategy Members

    /// <summary>
    /// Provides the ability for subclasses to control the initialisation method for the result class.
    /// </summary>
    /// <param name="pContext">the context of the generation</param>
    /// <param name="pProp">the property being generated</param>
    /// <returns>the initialisation expression</returns>
    protected override CodeExpression GetInitResultExpression(GenerationContext pContext, PropertyInfo pProp) {
      return new CodeObjectCreateExpression(typeof(List<>).MakeGenericType(GetElementType(pProp)));
    }

    /// <summary>
    /// Retrieves the first generic type argument of the lst.
    /// </summary>
    /// <param name="pProp">the property being generated</param>
    /// <returns>the element type</returns>
    protected override Type GetElementType(PropertyInfo pProp) {
      return pProp.PropertyType.GetGenericArguments()[0];
    }

    /// <summary>
    /// Retrieves a statement that adds the given converted type to the result.
    /// </summary>
    /// <param name="pContext">the generation context</param>
    /// <param name="pResult">the result object</param>
    /// <param name="pPosition">the expression to retrieve the position</param>
    /// <param name="pConverted">the expression containing the converted value</param>
    /// <returns>the statement to perform the storage of the converted result</returns>
    protected override CodeStatement GetConvertedResultStoreStatement(GenerationContext pContext, CodeExpression pResult, CodeExpression pPosition, CodeExpression pConverted) {
      return new CodeExpressionStatement(new CodeMethodInvokeExpression(pResult, "Add", pConverted));
    }


    /// <summary>
    /// Returns the code necessary to remove an item from the list cache.
    /// </summary>
    /// <param name="pContext">the generation context</param>
    /// <param name="pProp">the property being generated</param>
    /// <param name="pItemExpr">an expression to access the item being removed</param>
    protected override CodeStatement[] GetCacheRemoveStatements(GenerationContext pContext, PropertyInfo pProp,
                                                                CodeExpression pItemExpr) {
      CodeExpression cacheRef = MethodHelper.GenerateCacheExpression(pProp);

      return new CodeStatement[] {
         new CodeExpressionStatement(
           new CodeMethodInvokeExpression(cacheRef, "Remove", pItemExpr))
       };
    }

    /// <summary>
    /// Genetates a walk method for working through the list.
    /// </summary>
    /// <param name="pContext">the generation context</param>
    /// <param name="pProp">the property being generated</param>
    /// <param name="pHandleItemDelegate">the delegate to be called to check and action the removal</param>
    /// <returns></returns>
    protected override CodeStatement[] GenerateEnumerateForWalk(GenerationContext pContext, PropertyInfo pProp, GenerateHandleItemDelegate pHandleItemDelegate) {
      CodeExpression cacheRef = MethodHelper.GenerateCacheExpression(pProp);

      CodeVariableReferenceExpression indexerExpr = new CodeVariableReferenceExpression("i");
      CodeIterationStatement iterate = new CodeIterationStatement(
        new CodeVariableDeclarationStatement(typeof(int), "i", new CodePrimitiveExpression(0)),
        new CodeBinaryOperatorExpression(indexerExpr, CodeBinaryOperatorType.LessThan, new CodePropertyReferenceExpression(cacheRef, "Count")),
        new CodeAssignStatement(indexerExpr, new CodeBinaryOperatorExpression(indexerExpr, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1))));

      iterate.Statements.AddRange(pHandleItemDelegate(
        new CodeIndexerExpression(cacheRef, indexerExpr),
        new CodeStatement[] {
          new CodeExpressionStatement(new CodeMethodInvokeExpression(cacheRef, "RemoveAt", indexerExpr)),
          new CodeAssignStatement(indexerExpr, new CodeBinaryOperatorExpression(indexerExpr, CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1))),
//          new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(NotImplementedException)))
        }));

      return new CodeStatement[] {iterate};
    }
    #endregion
  }
}
